#include "WSyncHttpClient.h"
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

WSyncHttpClient::WSyncHttpClient() {
    // 初始化 OpenSSL
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
}

WSyncHttpClient::~WSyncHttpClient() {
    // 清理 OpenSSL
    EVP_cleanup();
    ERR_free_strings();
}

// 解析 URL 获取协议、主机、端口和路径
void WSyncHttpClient::parseUrl(const std::string& url, std::string& protocol, std::string& host, int& port, std::string& path) const {
    size_t protocolEnd = url.find("://");
    if (protocolEnd != std::string::npos) {
        protocol = url.substr(0, protocolEnd);
        size_t hostStart = protocolEnd + 3;
        size_t portStart = url.find(":", hostStart);
        size_t pathStart = url.find("/", hostStart);

        if (portStart != std::string::npos && (pathStart == std::string::npos || portStart < pathStart)) {
            host = url.substr(hostStart, portStart - hostStart);
            size_t portEnd = (pathStart != std::string::npos) ? pathStart : url.length();
            port = WHttpClientCommon::str2ll(url.substr(portStart + 1, portEnd - portStart - 1));
        } else {
            host = (pathStart != std::string::npos) ? url.substr(hostStart, pathStart - hostStart) : url.substr(hostStart);
            port = (protocol == "https") ? 443 : 80;
        }

        path = (pathStart != std::string::npos) ? url.substr(pathStart) : "/";
    }
}

// 拼接 URL
std::string WSyncHttpClient::buildUrl(const std::string& uri, const std::map<std::string, std::string>& querys) const {
    std::string url = uri;
    if (!querys.empty()) {
        url += "?";
        bool first = true;
        for (const auto& pair : querys) {
            if (!first) {
                url += "&";
            }
            url += WHttpClientCommon::urlEncode(pair.first) + "=" + WHttpClientCommon::urlEncode(pair.second);
            first = false;
        }
    }
    return url;
}

std::string WSyncHttpClient::getIpFromDNS(const std::string &dnsStr, bool isIpv6First)
{
    std::string ipv6Str = "";
    std::string ipv4Str = "";

    struct addrinfo hints;
    struct addrinfo *result, *rp;

    memset(&hints, 0, sizeof(struct addrinfo));
    // hints.ai_family = AF_INET; // 只返回ipv4地址
    hints.ai_family = AF_UNSPEC; // ipv4、ipv6地址都返回
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = 0;
    hints.ai_protocol = 0;          /* Any protocol */

    int s = getaddrinfo(dnsStr.c_str(), nullptr, &hints, &result);
    if (s != 0)
    {
        WLoge("WSyncHttpClient::getIpFromDNS getaddrinfo error: %s, reason: %s", dnsStr.c_str(), gai_strerror(s));
        return "";
    }

    char serverIp[46] = {0};
    for (rp = result; rp != NULL; rp = rp->ai_next)
    {
        if (rp->ai_family == AF_INET6)
        {
            sockaddr_in6 *sourceaddr = (sockaddr_in6 *)rp->ai_addr;
            inet_ntop(sourceaddr->sin6_family, &(sourceaddr->sin6_addr), serverIp, 46);
            if (ipv6Str.empty())
            {
                ipv6Str = serverIp;
            }
        }
        else
        {
            sockaddr_in* addr_in = (sockaddr_in*)rp->ai_addr;
            inet_ntop(addr_in->sin_family, &addr_in->sin_addr, serverIp, 16);
            if(ipv4Str.empty())
            {
                ipv4Str = serverIp;
            }
        }
    }

    freeaddrinfo(result);

    if (isIpv6First)
    {
        return ipv6Str.empty() ? ipv4Str : ipv6Str;
    }
    else
    {
        return ipv4Str.empty()? ipv6Str : ipv4Str;
    }
}

// 创建 HTTP 请求
std::string WSyncHttpClient::buildHttpRequest(const std::string& host, const HttpClientReqMsg& reqMsg) const {
    std::string url = buildUrl(reqMsg.uri, reqMsg.querys);
    std::string request = reqMsg.method + " " + url + " HTTP/1.1\r\n";
    request += "Host: " + host + "\r\n";
    for (const auto& pair : reqMsg.headers) {
        request += pair.first + ": " + pair.second + "\r\n";
    }
    if (!reqMsg.body.empty()) {
        request += "Content-Length: " + std::to_string(reqMsg.body.length()) + "\r\n";
    }
    request += "\r\n";
    request += reqMsg.body;
    return request;
}

// 解析响应头部和主体
void WSyncHttpClient::parseResponse(const std::string& response, std::shared_ptr<HttpClientRspMsg>& rspMsg, bool chunkFlag) const {
    WLogi("WSyncHttpClient::parseResponse whole response is %s", response.c_str());
    size_t bodyStart = response.find("\r\n\r\n");
    if (bodyStart != std::string::npos) {
        std::string headerStr = response.substr(0, bodyStart + 2);
        if (chunkFlag)
        {
            std::string initialChunk = response.substr(bodyStart + 4);
            if (!initialChunk.empty())
            {
                rspMsg->chunk = std::make_shared<std::string>(initialChunk);
                rspMsg->recvChunkSize = initialChunk.size();
            }
        }
        else
        {
            rspMsg->body = response.substr(bodyStart + 4);
        }

        size_t pos = 0;
        size_t headerOffset = 0;
        bool firstHeaderLineFlag = true;
        while ((pos = headerStr.find("\r\n", headerOffset)) != std::string::npos) {
            std::string line = headerStr.substr(headerOffset, pos - headerOffset + 1);
            if (firstHeaderLineFlag)
            {
                firstHeaderLineFlag = false;
                size_t spacePos1 = line.find(' ');
                size_t spacePos2 = line.find(' ', spacePos1 + 1);
                if ((spacePos1 == std::string::npos) || (spacePos2 == std::string::npos))
                {
                    rspMsg->errStatus = 9;
                    WLoge("WSyncHttpClient::parseResponse can not find right space in first line");
                    return;
                }
                std::string codeStr = line.substr(spacePos1 + 1, spacePos2 - spacePos1 - 1);
                rspMsg->code = WHttpClientCommon::str2ll(codeStr);
            }
            else
            {
                size_t colonPos = line.find(": ");
                int valueOffset = 2;
                if (colonPos == std::string::npos)
                {
                    colonPos = line.find(":");
                    valueOffset = 1;
                }

                if (colonPos != std::string::npos)
                {
                    std::string key = line.substr(0, colonPos);
                    std::string value = line.substr(colonPos + valueOffset);
                    rspMsg->headers[key] = value;
                }
            }
            headerOffset = pos + 2;
        }
    }
    else
    {
        WLoge("WSyncHttpClient::parseResponse can not find whole http header");
        rspMsg->errStatus = 10;
    }
}

std::string WSyncHttpClient::readResponseData(SSL *ssl, int sockfd, const std::string &protocol, size_t contentLength) const
{
    std::string response;
    char buffer[1024];
    int bytes;

    if (contentLength > 0)
    {
        size_t totalRead = 0;
        while (totalRead < contentLength) {
            if (protocol == "https") {
                bytes = SSL_read(ssl, buffer, std::min((size_t)sizeof(buffer), contentLength - totalRead));
            } else {
                bytes = recv(sockfd, buffer, std::min((size_t)sizeof(buffer), contentLength - totalRead), 0);
            }
            if (bytes <= 0) {
                break;
            }
            response.append(buffer, bytes);
            totalRead += bytes;
        }
    }
    else
    {
        if (protocol == "https")
        {
            while ((bytes = SSL_read(ssl, buffer, sizeof buffer)) > 0) {
                response.append(buffer, bytes);
            }
        }
        else
        {
            while ((bytes = recv(sockfd, buffer, sizeof buffer, 0)) > 0) {
                response.append(buffer, bytes);
            }
        }
    }

    return response;
}

WSyncHttpClient::ConnectionInfo WSyncHttpClient::createConnection(const std::string& host, int port, const std::string& protocol, int timeout)
{
    ConnectionInfo conn;

    std::string ip = getIpFromDNS(host);
    if (ip.empty()) {
        WLoge("WSyncHttpClient::createConnection getIpFromDNS failed");
        conn.errStatus = 1;
        return conn;
    }

    // 创建socket
    conn.sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (conn.sockfd == -1) {
        WLoge("WSyncHttpClient::createConnection Socket creation failed");
        conn.errStatus = 2;
        return conn;
    }

    // 设置服务器地址
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    inet_pton(AF_INET, ip.c_str(), &server_addr.sin_addr);

    // 设置超时
    struct timeval tv;
    tv.tv_sec = timeout;
    tv.tv_usec = 0;
    setsockopt(conn.sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
    setsockopt(conn.sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof tv);

    // 连接服务器
    if (connect(conn.sockfd, (struct sockaddr*)&server_addr, sizeof server_addr) == -1) {
        WLoge("WSyncHttpClient::createConnection Connection failed");
        close(conn.sockfd);
        conn.sockfd = -1;
        conn.errStatus = 3;
        return conn;
    }

    // HTTPS握手
    if (protocol == "https") {
        conn.ctx = SSL_CTX_new(TLS_client_method());
        if (!conn.ctx) {
            WLoge("WSyncHttpClient::createConnection SSL context creation failed");
            close(conn.sockfd);
            conn.sockfd = -1;
            conn.errStatus = 4;
            return conn;
        }

        conn.ssl = SSL_new(conn.ctx);
        if (!conn.ssl) {
            WLoge("WSyncHttpClient::createConnection SSL creation failed");
            SSL_CTX_free(conn.ctx);
            conn.ctx = nullptr;
            close(conn.sockfd);
            conn.sockfd = -1;
            conn.errStatus = 5;
            return conn;
        }

        if (SSL_set_fd(conn.ssl, conn.sockfd) != 1) {
            WLoge("WSyncHttpClient::createConnection SSL set fd failed");
            SSL_free(conn.ssl);
            conn.ssl = nullptr;
            SSL_CTX_free(conn.ctx);
            conn.ctx = nullptr;
            close(conn.sockfd);
            conn.sockfd = -1;
            conn.errStatus = 6;
            return conn;
        }

        if (SSL_connect(conn.ssl) != 1) {
            WLoge("WSyncHttpClient::createConnection SSL connect failed");
            SSL_free(conn.ssl);
            conn.ssl = nullptr;
            SSL_CTX_free(conn.ctx);
            conn.ctx = nullptr;
            close(conn.sockfd);
            conn.sockfd = -1;
            conn.errStatus = 7;
            return conn;
        }
    }

    return conn;
}

int WSyncHttpClient::sendHttpRequest(ConnectionInfo& conn, const std::string& protocol, const std::string& host, const HttpClientReqMsg& reqMsg)
{
    std::string request = buildHttpRequest(host, reqMsg);
    int sentBytes = -1;

    if (protocol == "https") {
        sentBytes = SSL_write(conn.ssl, request.c_str(), request.length());
    } else {
        sentBytes = send(conn.sockfd, request.c_str(), request.length(), 0);
    }

    if (sentBytes != (int)request.length()) {
        WLoge("WSyncHttpClient::sendHttpRequest Request send failed");
        return -1;
    }
    return 0;
}

std::shared_ptr<HttpClientRspMsg> WSyncHttpClient::sendRequest(const std::string& url, const HttpClientReqMsg& reqMsg, int timeout)
{
    std::string protocol, host;
    int port;
    std::string path;
    parseUrl(url, protocol, host, port, path);
    std::shared_ptr<HttpClientRspMsg> rspMsg = std::make_shared<HttpClientRspMsg>();

    // 建立连接
    ConnectionInfo conn = createConnection(host, port, protocol, timeout);
    if (conn.errStatus != 0) {
        rspMsg->errStatus = conn.errStatus;
        return rspMsg;
    }

    // 发送请求
    if (sendHttpRequest(conn, protocol, host, reqMsg) != 0) {
        // 清理资源
        if (protocol == "https") {
            SSL_free(conn.ssl);
            SSL_CTX_free(conn.ctx);
        }
        close(conn.sockfd);
        rspMsg->errStatus = 12;
        return rspMsg;
    }

    // 读取响应（原有逻辑保留，仅修改资源引用）
    std::string fullResponse;
    int64_t currentBodySize = 0;
    char buffer[1024];
    bool foundEnd = false;

    while (!foundEnd) {
        int bytes = 0;
        if (protocol == "https") {
            bytes = SSL_read(conn.ssl, buffer, sizeof(buffer));
        } else {
            bytes = recv(conn.sockfd, buffer, sizeof(buffer), 0);
        }

        if (bytes <= 0) {
            break;
        }

        fullResponse.append(buffer, bytes);

        // 检查头部结束标记
        size_t endPos = fullResponse.find("\r\n\r\n");
        if (endPos != std::string::npos) {
            if (endPos + 4 < fullResponse.size()) {
                currentBodySize = fullResponse.size() - (endPos + 4);
            }
            foundEnd = true;
        }
    }

    if (fullResponse.empty()) {
        WLoge("WSyncHttpClient::sendRequest timeout");
        rspMsg->errStatus = 8;
    } else {
        // 处理Content-Length和剩余数据
        int64_t contentLength = -1;
        size_t pos = fullResponse.find("Content-Length:");
        if (pos != std::string::npos) {
            pos += 15;
            size_t endPos = fullResponse.find("\r\n", pos);
            if (endPos != std::string::npos) {
                contentLength = WHttpClientCommon::str2ll(fullResponse.substr(pos, endPos - pos));
            }
        }
        contentLength = (contentLength < 0) ? 0 : contentLength;

        if (currentBodySize < contentLength) {
            fullResponse += readResponseData(conn.ssl, conn.sockfd, protocol, contentLength - currentBodySize);
        }

        parseResponse(fullResponse, rspMsg);
        if (contentLength > 0 && contentLength != (int64_t)rspMsg->body.size() && rspMsg->errStatus == 0) {
            rspMsg->errStatus = 11;
            WLoge("WSyncHttpClient::sendRequest incomplete body");
        }
        rspMsg->totalBodySize = (contentLength == 0) ? rspMsg->body.size() : contentLength;
    }

    // 清理资源
    if (protocol == "https") {
        SSL_shutdown(conn.ssl);
        SSL_free(conn.ssl);
        SSL_CTX_free(conn.ctx);
    }
    close(conn.sockfd);

    return rspMsg;
}

void WSyncHttpClient::sendRequestToDwFile(std::string url, const HttpClientReqMsg& reqMsg, HttpClientCbFun cbFun, int timeout)
{
    std::string protocol, host;
    int port;
    std::string path;
    parseUrl(url, protocol, host, port, path);
    std::shared_ptr<HttpClientRspMsg> rspMsg = std::make_shared<HttpClientRspMsg>();

    // 建立连接
    ConnectionInfo conn = createConnection(host, port, protocol, timeout);
    if (conn.errStatus != 0) {
        rspMsg->errStatus = conn.errStatus;
        cbFun(rspMsg);
        return;
    }

    // 发送请求
    if (sendHttpRequest(conn, protocol, host, reqMsg) != 0) {
        rspMsg->errStatus = 12; // 发送失败错误码
        if (protocol == "https") {
            SSL_free(conn.ssl);
            SSL_CTX_free(conn.ctx);
        }
        close(conn.sockfd);
        cbFun(rspMsg);
        return;
    }

    // 读取响应头部（原有逻辑保留，修改资源引用）
    std::string headerBuffer;
    char buffer[1024];
    bool headerComplete = false;
    int64_t contentLength = -1;

    while (!headerComplete) {
        int bytes = 0;
        if (protocol == "https") {
            bytes = SSL_read(conn.ssl, buffer, sizeof(buffer));
        } else {
            bytes = recv(conn.sockfd, buffer, sizeof(buffer), 0);
        }

        if (bytes <= 0) {
            WLoge("WSyncHttpClient::sendRequestToDwFile read header failed");
            rspMsg->errStatus = 8;
            // 清理资源
            if (protocol == "https") {
                SSL_free(conn.ssl);
                SSL_CTX_free(conn.ctx);
            }
            close(conn.sockfd);
            cbFun(rspMsg);
            return;
        }

        headerBuffer.append(buffer, bytes);
        size_t endPos = headerBuffer.find("\r\n\r\n");
        if (endPos != std::string::npos) {
            headerComplete = true;
            parseResponse(headerBuffer, rspMsg, true);
            if (rspMsg->headers.find("Content-Length") != rspMsg->headers.end()) {
                contentLength = WHttpClientCommon::str2ll(rspMsg->headers["Content-Length"]);
            }

            if (rspMsg->chunk.get())
            {
                cbFun(rspMsg);
                rspMsg->chunk.reset();
            }
        }
    }

    while (true) {
        // contentLength为0时可能是chunk类型的下载，这种情况下通过读到的bytes等于0结束循环
        if (contentLength > 0 && rspMsg->recvChunkSize >= contentLength) {
            break;
        }

        int bytes = 0;
        if (protocol == "https") {
            bytes = SSL_read(conn.ssl, buffer, sizeof(buffer));
        } else {
            bytes = recv(conn.sockfd, buffer, sizeof(buffer), 0);
        }

        if (bytes <= 0) {
            if (bytes < 0) {
                WLoge("WSyncHttpClient::sendRequestToDwFile read body error");
                rspMsg->errStatus = 13;
            }
            break;
        }

        rspMsg->chunk = std::make_shared<std::string>(buffer, bytes);
        rspMsg->recvChunkSize += bytes;
        cbFun(rspMsg);
        rspMsg->chunk.reset();
    }

    // 最后还需要一次回调，让外部知道接收数据完成了并且清理资源
    rspMsg->finishRecvChunk = true;
    rspMsg->chunk = std::make_shared<std::string>();
    cbFun(rspMsg);

    if (protocol == "https") {
        SSL_shutdown(conn.ssl);
        SSL_free(conn.ssl);
        SSL_CTX_free(conn.ctx);
    }
    close(conn.sockfd);

    //检查完整性
    if (contentLength > 0 && rspMsg->recvChunkSize != contentLength) {
        WLoge("WSyncHttpClient::sendRequestToDwFile incomplete file data");
    }
}
